#include "SkyboxWidget.h"

#include <QMouseEvent>
#include <QWheelEvent>

QOpenGLSkyboxWidget::QOpenGLSkyboxWidget(const QString& frontImagePath,
    const QString& backImagePath,
    const QString& topImagePath,
    const QString& bottomImagePath,
    const QString& leftImagePath,
    const QString& rightImagePath,
    QWidget *parent) :
    QOpenGLWidget{ parent },
    mTexture(QOpenGLTexture::TargetCubeMap),
    mFrontImagePath(frontImagePath),
    mBackImagePath(backImagePath),
    mTopImagePath(topImagePath),
    mBottomImagePath(bottomImagePath),
    mLeftImagePath(leftImagePath),
    mRightImagePath(rightImagePath)
{
  
}

QOpenGLSkyboxWidget::~QOpenGLSkyboxWidget()
{
    makeCurrent();
    mTexture.destroy();
    glDeleteBuffers(1, &vbo);
    glDeleteVertexArrays(1, &vao);
    doneCurrent();
}

QImage normalizeSquaredImage(QImage image)
{
    int imageSize = std::min(image.width(), image.height());
    QRect rect = QRect(
        (image.width() - imageSize) / 2,
        (image.height() - imageSize) / 2,
        imageSize,
        imageSize
    );

    return image.copy(rect);
}

void QOpenGLSkyboxWidget::loadImages()
{
    const QImage posx = normalizeSquaredImage(QImage(mRightImagePath)).convertToFormat(QImage::Format_RGBA8888);
    const QImage negx = normalizeSquaredImage(QImage(mLeftImagePath)).convertToFormat(QImage::Format_RGBA8888);

    const QImage posy = normalizeSquaredImage(QImage(mTopImagePath)).convertToFormat(QImage::Format_RGBA8888);
    const QImage negy = normalizeSquaredImage(QImage(mBottomImagePath)).convertToFormat(QImage::Format_RGBA8888);

    const QImage posz = normalizeSquaredImage(QImage(mFrontImagePath)).convertToFormat(QImage::Format_RGBA8888);
    const QImage negz = normalizeSquaredImage(QImage(mBackImagePath)).convertToFormat(QImage::Format_RGBA8888);

    mTexture.create();
    mTexture.setSize(posx.width(), posx.height(), posx.depth());

    mTexture.setFormat(QOpenGLTexture::RGBA8_UNorm);
    mTexture.allocateStorage();

    mTexture.setData(0, 0, QOpenGLTexture::CubeMapPositiveX,
        QOpenGLTexture::RGBA, QOpenGLTexture::UInt8,
        posx.constBits(), Q_NULLPTR);

    mTexture.setData(0, 0, QOpenGLTexture::CubeMapPositiveY,
        QOpenGLTexture::RGBA, QOpenGLTexture::UInt8,
        posy.constBits(), Q_NULLPTR);

    mTexture.setData(0, 0, QOpenGLTexture::CubeMapPositiveZ,
        QOpenGLTexture::RGBA, QOpenGLTexture::UInt8,
        posz.constBits(), Q_NULLPTR);

    mTexture.setData(0, 0, QOpenGLTexture::CubeMapNegativeX,
        QOpenGLTexture::RGBA, QOpenGLTexture::UInt8,
        negx.constBits(), Q_NULLPTR);

    mTexture.setData(0, 0, QOpenGLTexture::CubeMapNegativeY,
        QOpenGLTexture::RGBA, QOpenGLTexture::UInt8,
        negy.constBits(), Q_NULLPTR);

    mTexture.setData(0, 0, QOpenGLTexture::CubeMapNegativeZ,
        QOpenGLTexture::RGBA, QOpenGLTexture::UInt8,
        negz.constBits(), Q_NULLPTR);

    mTexture.setWrapMode(QOpenGLTexture::ClampToEdge);
    mTexture.setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
    mTexture.setMagnificationFilter(QOpenGLTexture::LinearMipMapLinear);
}

void QOpenGLSkyboxWidget::initializeGL()
{
    initializeOpenGLFunctions();

    std::printf("%s \n", glGetString(GL_VERSION));
    std::printf("%s \n", glGetString(GL_SHADING_LANGUAGE_VERSION));

    mProgram.addShaderFromSourceCode(
        QOpenGLShader::Vertex,
        R"(
                #version 330 core
                layout (location = 0) in vec3 aPosition;


               out vec3 TexCoords;

                void main()
                {
                    gl_Position =  vec4(aPosition, 1.0);
                    TexCoords = aPosition;
                }
                )");

    mProgram.addShaderFromSourceCode(
        QOpenGLShader::Fragment,
        R"(
                #version 330 core
                in vec3 TexCoords;

                out vec4 FragColor;

                uniform samplerCube skybox;

                void main()
                {
                    //FragColor =  textureLod(skybox, TexCoords,0);
                    FragColor =  texture(skybox, TexCoords);
                }
                )");

    mProgram.link();
    mProgram.bind();
    mProgram.setUniformValue("skybox", 0);

    loadImages();

    GLfloat vertices[] = {
        -1.0f,  1.0f, -1.0f,
        -1.0f, -1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,
         1.0f,  1.0f, -1.0f,
        -1.0f,  1.0f, -1.0f,

        -1.0f, -1.0f,  1.0f,
        -1.0f, -1.0f, -1.0f,
        -1.0f,  1.0f, -1.0f,
        -1.0f,  1.0f, -1.0f,
        -1.0f,  1.0f,  1.0f,
        -1.0f, -1.0f,  1.0f,

         1.0f, -1.0f, -1.0f,
         1.0f, -1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,

        -1.0f, -1.0f,  1.0f,
        -1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f, -1.0f,  1.0f,
        -1.0f, -1.0f,  1.0f,

        -1.0f,  1.0f, -1.0f,
         1.0f,  1.0f, -1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
        -1.0f,  1.0f,  1.0f,
        -1.0f,  1.0f, -1.0f,

        -1.0f, -1.0f, -1.0f,
        -1.0f, -1.0f,  1.0f,
         1.0f, -1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,
        -1.0f, -1.0f,  1.0f,
         1.0f, -1.0f,  1.0f
    };

    glGenVertexArrays(1, &vao);
    glGenBuffers(1, &vbo);

    glBindVertexArray(vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);


    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}

void QOpenGLSkyboxWidget::paintGL()
{
    glClearColor(1.0f, 1.0f, 0.0f, 1.0f); // Set background color
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glBindVertexArray(vao);
    mProgram.bind();
    mTexture.bind();

    glDepthFunc(GL_LEQUAL);
    glDrawArrays(GL_TRIANGLES,
        0,
        36);
    glDepthFunc(GL_LESS);

    mTexture.release();
    mProgram.release();

    glBindVertexArray(0);
}

void QOpenGLSkyboxWidget::resizeGL(int w, int h)
{
    glViewport(0, 0, w, h);
}
